package zhuyouyong.bottomtab

import android.animation.ObjectAnimator
import android.content.Context
import android.graphics.Color
import android.graphics.Outline
import android.graphics.Path
import android.graphics.PointF
import android.util.AttributeSet
import android.util.TypedValue
import android.view.Gravity
import android.view.View
import android.view.ViewGroup.LayoutParams.MATCH_PARENT
import android.view.ViewOutlineProvider
import android.widget.ImageView
import androidx.core.animation.doOnEnd
import androidx.core.content.ContextCompat
import kotlin.math.abs

/**
 * @author zhuyouyong <zhuyouyong@hangsheng.com.cn>
 * Created on 2021/5/6.
 */
class BottomTab(context: Context, attrs: AttributeSet? = null) : CustomLayout(context, attrs) {
    private val tabHeight = 84.dp
    private val itemHeight = 60.dp
    private var itemWidth = 0
    private val circle = BorderImageView(context).apply {
        scaleType = ImageView.ScaleType.CENTER
        setBackgroundColor(ContextCompat.getColor(context, R.color.zyy_bg_color))
        layoutParams = LayoutParams(MATCH_PARENT, MATCH_PARENT)
        clipToOutline = true
        outlineProvider = object : ViewOutlineProvider() {
            override fun getOutline(view: View, outline: Outline) {
                outline.setOval(0, 0, view.width, view.height)
            }
        }
        addView(this)
    }
    private val tabBg = TabBg(context).apply {
        layoutParams = LayoutParams(MATCH_PARENT, itemHeight)
        addView(this)
    }
    private lateinit var itemResources: Array<Int?>
    private var disableClick = false
    private var clickListener: ((Int) -> Unit)? = null
    private val path = Path()
    private val start = PointF(0f, 0f)
    private val quad = PointF(0f, 0f)
    private val end = PointF(0f, 0f)
    private var currentPos = 0
    private var holdPos = 0
    private var circleImageResource = -1

    init {
        items.forEachIndexed { index, labelTextView ->
            labelTextView.setOnClickListener { onClick(index) }
        }
        items[0].translationY = itemHeight.toFloat()
    }

    override fun setContent(vararg contents: Pair<String, Int>) {
        items = Array(contents.size) {
            LabelTextView(context).apply {
                textSize = 4.dp.toFloat()
                setTextColor(Color.BLACK)
                gravity = Gravity.CENTER_HORIZONTAL
                setPadding(0, 12.dp, 0, 0)
                layoutParams = LayoutParams(MATCH_PARENT, itemHeight)
                /*setBackgroundResource(context.resourceId(android.R.attr.selectableItemBackground))*/
                addView(this)
            }
        }
        itemResources = arrayOfNulls(contents.size)
        contents.forEachIndexed { index, pair ->
            itemResources[index] = pair.second
            (items[index] as LabelTextView).apply {
                text = pair.first
                setCompoundDrawablesWithIntrinsicBounds(0, pair.second!!, 0, 0)
            }
        }
        circle.setImageResource(contents[0].second!!)
    }

    fun setLabel(
        index: Int, drawableId: Int,
        paddingStart: Float = 8.dp.toFloat(), paddingTop: Float = 6.dp.toFloat()
    ) {
        (items[index] as LabelTextView).setLabel(drawableId, paddingStart, paddingTop)
    }

    fun setBorderColor(color: Int) {
        circle.setBorderColor(color)
        tabBg.setBorderColor(color)
    }

    override fun setClickListener(click: (Int) -> Unit) {
        clickListener = click
    }

    private fun onClick(pos: Int) {
        if (disableClick || currentPos == pos) return
        disableClick = true
        circleImageResource = itemResources[pos]!!
        makePath(pos)
        ObjectAnimator.ofFloat(circle, TRANSLATION_X, TRANSLATION_Y, path).apply {
            val ratio = pos - currentPos
            holdPos = currentPos
            addUpdateListener {
                if (animatedFraction <= 0f) return@addUpdateListener
                if (animatedFraction > 0.5f) updateCircleImg()
                val progress = animatedFraction * ratio
                tabBg.onProgress(progress)
                val plus = if (progress > 0f) 1 else -1
                val positiveProgress = abs(progress)
                when {
                    positiveProgress <= 1f -> {
                        items[holdPos].translationY = (1 - positiveProgress) * itemHeight
                        items[holdPos + plus].translationY = positiveProgress * itemHeight
                    }
                    positiveProgress <= 2f -> {
                        items[holdPos].translationY = 0f
                        items[holdPos + plus].translationY = (2 - positiveProgress) * itemHeight
                        items[holdPos + plus*2].translationY = (positiveProgress - 1) * itemHeight
                    }
                    positiveProgress <= 3f -> {
                        items[holdPos + plus].translationY = 0f
                        items[holdPos + plus*2].translationY = (3 - positiveProgress) * itemHeight
                        items[holdPos + plus*3].translationY = (positiveProgress - 2) * itemHeight
                    }
                }
            }
            doOnEnd {
                adjust(pos)
                tabBg.onEnd(pos)
                disableClick = false
                post { clickListener?.invoke(pos) }
            }
            duration = 500L + 50L * (abs(ratio) - 1)
            start()
        }
        currentPos = pos
    }

    private fun adjust(pos: Int) {
        items.forEachIndexed { index, labelTextView ->
            if (index == pos) {
                labelTextView.translationY = itemHeight.toFloat()
            } else {
                labelTextView.translationY = 0f
            }
        }
    }

    private fun updateCircleImg() {
        if (circleImageResource > 0) {
            circle.setImageResource(circleImageResource)
            circleImageResource = -1
        }
    }

    private fun makePath(pos: Int) {
        start.x = (currentPos * itemWidth).toFloat()
        val differ = pos - currentPos
        val quadStep = (differ * itemWidth) shr 1
        quad.x = start.x + quadStep
        end.x = quad.x + quadStep
        path.reset()
        path.moveTo(start.x, start.y)
        path.quadTo(quad.x, quad.y, end.x, end.y)
    }

    override fun getCurrentPos() = currentPos

    private fun Context.resourceId(attr: Int) = TypedValue().let {
        theme.resolveAttribute(attr, it, true)
        it.resourceId
    }

    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec)
        itemWidth = measuredWidth shr 2
        val circleSpec = (itemWidth / 1.8f).toInt().toExactlyMeasureSpec()
        circle.measure(circleSpec, circleSpec)
        tabBg.autoMeasure()
        val itemWidthSpec = itemWidth.toExactlyMeasureSpec()
        val itemHeightSpec = itemHeight.toExactlyMeasureSpec()
        items.forEach { it.measure(itemWidthSpec, itemHeightSpec) }
        setMeasuredDimension(measuredWidth, tabHeight)
        quad.y = (tabHeight - (circle.measuredHeight shr 1)) * 2f
    }

    override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
        circle.layout((itemWidth - circle.measuredWidth) shr 1, 0)
        val y = tabHeight - itemHeight
        tabBg.layout(0, y)
        items.forEachIndexed { index, labelTextView ->
            val previous = index - 1
            val x = if (previous >= 0) items[previous].right else 0
            labelTextView.layout(x, y)
        }
    }
}